{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题：**\n",
    "假设你正在为一个电影推荐系统设计一个简单的KNN算法。我们有以下一些用户的电影评分数据，数据由两个特征组成：用户对电影A和电影B的评分，分别在1-5之间。用户的标签（电影类型偏好）是动作片（标签0）或者是喜剧片（标签1）。我们有一个新用户，他给电影A评分为3，电影B评分为4。请问这个用户可能偏好哪种类型的电影？\n",
    "\n",
    "**数据：**\n",
    "\n",
    "| 用户   | 电影A评分 | 电影B评分 | 偏好类型 |\n",
    "| ------ | --------- | --------- | -------- |\n",
    "| 用户1  | 5         | 1         | 动作片   |\n",
    "| 用户2  | 4         | 2         | 动作片   |\n",
    "| 用户3  | 2         | 5         | 喜剧片   |\n",
    "| 用户4  | 1         | 4         | 喜剧片   |\n",
    "| 用户5  | 3         | 2         | 动作片   |\n",
    "| 用户6  | 2         | 5         | 喜剧片   |\n",
    "\n",
    "你需要做以下步骤：\n",
    "1. 构造数据\n",
    "2. 创建KNN模型\n",
    "3. 使用数据训练模型\n",
    "4. 预测新用户的喜好"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 0. 引入核心包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np  # 用于数值计算和数组操作\n",
    "import matplotlib.pyplot as plt  # 用于数据可视化\n",
    "from sklearn.neighbors import KNeighborsClassifier  # 导入K近邻分类算法"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = np.array([[5, 1], [4, 2], [2, 5], [1, 4], [3, 2], [2, 5]])\n",
    "y = np.array([0, 0, 1, 1, 0, 1])  # 0表示动作片，1表示喜剧片"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 创建 KNN 模型\n",
    "k = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "k = 1\n",
    "knn = KNeighborsClassifier(n_neighbors=k)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  display: none;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  overflow: visible;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".estimator-table summary {\n",
       "    padding: .5rem;\n",
       "    font-family: monospace;\n",
       "    cursor: pointer;\n",
       "}\n",
       "\n",
       ".estimator-table details[open] {\n",
       "    padding-left: 0.1rem;\n",
       "    padding-right: 0.1rem;\n",
       "    padding-bottom: 0.3rem;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table {\n",
       "    margin-left: auto !important;\n",
       "    margin-right: auto !important;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(odd) {\n",
       "    background-color: #fff;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(even) {\n",
       "    background-color: #f6f6f6;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:hover {\n",
       "    background-color: #e0e0e0;\n",
       "}\n",
       "\n",
       ".estimator-table table td {\n",
       "    border: 1px solid rgba(106, 105, 104, 0.232);\n",
       "}\n",
       "\n",
       ".user-set td {\n",
       "    color:rgb(255, 94, 0);\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td.value pre {\n",
       "    color:rgb(255, 94, 0) !important;\n",
       "    background-color: transparent !important;\n",
       "}\n",
       "\n",
       ".default td {\n",
       "    color: black;\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td i,\n",
       ".default td i {\n",
       "    color: black;\n",
       "}\n",
       "\n",
       ".copy-paste-icon {\n",
       "    background-image: url();\n",
       "    background-repeat: no-repeat;\n",
       "    background-size: 14px 14px;\n",
       "    background-position: 0;\n",
       "    display: inline-block;\n",
       "    width: 14px;\n",
       "    height: 14px;\n",
       "    cursor: pointer;\n",
       "}\n",
       "</style><body><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KNeighborsClassifier(n_neighbors=1)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>KNeighborsClassifier</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.7/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\" data-param-prefix=\"\">\n",
       "        <div class=\"estimator-table\">\n",
       "            <details>\n",
       "                <summary>Parameters</summary>\n",
       "                <table class=\"parameters-table\">\n",
       "                  <tbody>\n",
       "                    \n",
       "        <tr class=\"user-set\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_neighbors',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_neighbors&nbsp;</td>\n",
       "            <td class=\"value\">1</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('weights',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">weights&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;uniform&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('algorithm',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">algorithm&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;auto&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('leaf_size',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">leaf_size&nbsp;</td>\n",
       "            <td class=\"value\">30</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('p',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">p&nbsp;</td>\n",
       "            <td class=\"value\">2</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;minkowski&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric_params',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric_params&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_jobs',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_jobs&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "                  </tbody>\n",
       "                </table>\n",
       "            </details>\n",
       "        </div>\n",
       "    </div></div></div></div></div><script>function copyToClipboard(text, element) {\n",
       "    // Get the parameter prefix from the closest toggleable content\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text;\n",
       "\n",
       "    const originalStyle = element.style;\n",
       "    const computedStyle = window.getComputedStyle(element);\n",
       "    const originalWidth = computedStyle.width;\n",
       "    const originalHTML = element.innerHTML.replace('Copied!', '');\n",
       "\n",
       "    navigator.clipboard.writeText(fullParamName)\n",
       "        .then(() => {\n",
       "            element.style.width = originalWidth;\n",
       "            element.style.color = 'green';\n",
       "            element.innerHTML = \"Copied!\";\n",
       "\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        })\n",
       "        .catch(err => {\n",
       "            console.error('Failed to copy:', err);\n",
       "            element.style.color = 'red';\n",
       "            element.innerHTML = \"Failed!\";\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        });\n",
       "    return false;\n",
       "}\n",
       "\n",
       "document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) {\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const paramName = element.parentElement.nextElementSibling.textContent.trim();\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName;\n",
       "\n",
       "    element.setAttribute('title', fullParamName);\n",
       "});\n",
       "</script></body>"
      ],
      "text/plain": [
       "KNeighborsClassifier(n_neighbors=1)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "knn.fit(X, y)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 用模型推理(预测)用户的喜好"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_user = np.array([[3, 4]])\n",
    "prediction = knn.predict(new_user)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 数据可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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ReLtRo0aJbVq1XJ3MQBkAEERgDhPPfWUCVKee+65RPf169fP3KfHOFNERES8lbczonXr1mZ9KV1ENbMDucGDByda4wmAdyEoAhBPiRIlZN68eXLz5s24fbdu3ZK5c+dKyZIlnf58ulp2aGhohs9z/Phx2bRpk1kUd9asWWl+vC7EqYuvplfOnDklf/786X48APcjKAIQT506dUxg9O2338bt0981IKpdu3a8Y3X19pdfflkKFSokWbNmlfvuuy+ulUYDjOLFi8u0adPiPWbnzp0SGBgox44dc9jqcuLECXn88cclT548ki9fPunUqZP89ddfKZb7008/lYceekief/55+fLLL+MFdY7Mnj3bPMfixYulSpUqJjDTwErL37JlSylQoIDkzp1bmjRpIjt27Ih7XOnSpc3Phx9+2JTddjth95m2qHXu3Fneeecds4K4Bkza2hYVFRWvlax9+/aSLVs2KVOmjAk89Xzvv/9+ivUF4HwERQAS6d27twkybLTl5emnn0503NChQ+Wbb76ROXPmmMChXLlypgvr4sWLJvDp2rWr+aK398UXX0jjxo2lVKlSic6nAYM+PleuXPLzzz/Lxo0bTQtMmzZt5M6dO0mWV5dw1PL26NFDKlWqZMqxYMGCFOsZGRkpEyZMkBkzZsjevXtNcHft2jXp1auXbNiwQbZs2SLly5eXdu3amf3KFvTp82lQk1xX3U8//SRHjhwxP/U10kBMN5uePXvK6dOnZe3ateZ1/Pjjj+XcuXMplhuAi7h7RVoAnkNXt+7UqZPl3LlzltDQUMtff/1ltqxZs1r+/vtvc59tBezr169bQkJCLF988UXc4+/cuWO56667LP/+97/N7Z07d1oCAgIsx44dM7djYmLMytrTpk2Le4z+N7Rw4ULz++eff26pWLGiJTY2Nu7+27dvW7Jly2ZZvnx5kuXWleR1VfmoqChze9KkSWa17uR8+umn5rl37dqV7HFa5ly5clm+//57h2W2GTlypKVmzZrxXktd8T46OjpuX5cuXcyq4mr//v3mPFu3bo27/9ChQ2aflh9A5qOlCEAiBQsWNN062qqhLSL6u3Yn2dMWEG3Z0VYfm5CQEKlfv77s37/f3NbupMqVK8e1Fq1bt860hHTp0sXh8/72229y+PBh01KkLUS6aRea5jTp8yVFW7KeeOIJCQ4ONre1hUpbmZJ7jMqSJYvUqFEj3r6zZ8/KM888Y1qItPssLCxMrl+/brrW0qpq1aom8dtGu9FsLUEHDhww5dXuShtt4cqbN2+anweAc1j/BwEAB11omrSspk6dmu7zdO/e3QRFw4YNMz+1KyyphGQNPurWrWu62BwFao5oV93ChQtNgGafv6SJ0xosjR07NsmyaS6P5gXZ066zCxcuyOTJk00Xn+YaNWzYMNnuu6RokGhPnysjydwAXIuWIgAO2fJ4bHk+CZUtW9a0tGiLjI0eqzk2mrhs061bN9mzZ49s377d5PlokJQUbTU5dOiQye3RVhP7TVttHNEAShO6tZVp165dcdu7775rWro0OEoLrY8mj2sekbb0aFB0/vz5RMFOWs+bUMWKFSU6OtoknttoK9mlS5cydF4A6UdQBMAh7fbRbrB9+/bF6wKyyZEjhxnpNWTIEPnxxx/NcdrtpMnLffr0iTtOR1M1atTI7NNAomPHjkk+pwZM2k2nI8400fro0aMmCVmDlJMnTzp8zMyZM+Wxxx6TatWqxdv0+TSY0bKlhXabff7556buv/zyiymTtijZ0zrpnERnzpxJdxCjCeEtWrSQZ599Vn799VcTHOnvjlqvAGQOgiIASdJ8Gt2SMn78eHn00UflySefNK082tKxfPnyRHkxGlhoS44OY08YYNjLnj27rF+/3gz/f+SRR0w+kgY3mlPkqBza+qTn1TIkpC1LzZs3N0FTWujxGuhofbRetikH7Gkr1MqVK83UBQmnKUiLzz77TAoXLiwPPPCAeW00qNR8Kp3eAEDmC9Bsazc8LwAgAW0N00Br1apVJqADkLkIigDATdasWWOSy6tXr27mPNJ5n06dOiUHDx5MlKQNwPUYfQYAbqKJ6SNGjJA///zTdJtp7pUmjhMQAe5BSxEAAACJ1gAAAFYERQAAAARFAAAAVgRFAAAABEUAAABWBEUAAAAERQAAAFYERQAAAARFAAAAYvw/XdU37X5ykrUAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "新用户可能偏好的电影类型： 喜剧片\n"
     ]
    }
   ],
   "source": [
    "plt.title(\"User Movie Preference\", size=15)\n",
    "plt.xlabel(\"Movie A rating\")\n",
    "plt.ylabel(\"Movie B rating\")\n",
    "plt.grid()\n",
    "\n",
    "# 绘制原始样本点，不同的影片喜好类别用不同的颜色标记\n",
    "plt.scatter(X[:, 0], X[:, 1], c=y)\n",
    "\n",
    "# 绘制新数据点，用红色x标记，大小为8\n",
    "plt.plot(new_user[0, 0], new_user[0, 1], marker='x', color='red', markersize=8)\n",
    "\n",
    "# 新数据最近邻索引为第一个最近邻的索引\n",
    "dist, idx = knn.kneighbors(new_user)\n",
    "nearest = X[idx[0]]  # 获取最近邻点的坐标\n",
    "\n",
    "# 用红线标记新数据点与最近邻点的连接线\n",
    "plt.plot([new_user[0, 0], nearest[0, 0]], [new_user[0, 1], nearest[0, 1]], 'r--')\n",
    "\n",
    "# 为每个点添加坐标文本\n",
    "for x, y_val in zip(X[:, 0], X[:, 1]):\n",
    "    plt.text(x, y_val + 0.1, f'({x}, {y_val})')\n",
    "\n",
    "# 为新数据点添加坐标文本\n",
    "plt.text(new_user[0, 0], new_user[0, 1] + 0.1, f'({new_user[0, 0]}, {new_user[0, 1]})')\n",
    "plt.show()\n",
    "\n",
    "# 输出预测结果\n",
    "print(\"新用户可能偏好的电影类型：\", \"喜剧片\" if prediction[0] == 1 else \"动作片\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
